Go 语言并没有设计诸如虚函数、纯虚函数、继承、多重继承等概念,但它通过接口却非常优雅地支持了面向对象的特性。

多态是一种运行期的行为,它有以下几个特点:

  1. 一种类型具有多种类型的能力
  2. 允许不同的对象对同一消息做出灵活的反应
  3. 以一种通用的方式对待个使用的对象
  4. 非动态语言必须通过继承和接口的方式来实现

看一个实现了多态的代码例子:

  1. package main
  2. import "fmt"
  3. func main() {
  4. qcrao := Student{age: 18}
  5. whatJob(&qcrao)
  6. growUp(&qcrao)
  7. fmt.Println(qcrao)
  8. stefno := Programmer{age: 100}
  9. whatJob(stefno)
  10. growUp(stefno)
  11. fmt.Println(stefno)
  12. }
  13. func whatJob(p Person) {
  14. p.job()
  15. }
  16. func growUp(p Person) {
  17. p.growUp()
  18. }
  19. type Person interface {
  20. job()
  21. growUp()
  22. }
  23. type Student struct {
  24. age int
  25. }
  26. func (p Student) job() {
  27. fmt.Println("I am a student.")
  28. return
  29. }
  30. func (p *Student) growUp() {
  31. p.age += 1
  32. return
  33. }
  34. type Programmer struct {
  35. age int
  36. }
  37. func (p Programmer) job() {
  38. fmt.Println("I am a programmer.")
  39. return
  40. }
  41. func (p Programmer) growUp() {
  42. // 程序员老得太快 ^_^
  43. p.age += 10
  44. return
  45. }

代码里先定义了 1 个 Person 接口,包含两个函数:

  1. job()
  2. growUp()

然后,又定义了 2 个结构体,StudentProgrammer,同时,类型 *StudentProgrammer 实现了 Person 接口定义的两个函数。注意,*Student 类型实现了接口, Student 类型却没有。

之后,我又定义了函数参数是 Person 接口的两个函数:

  1. func whatJob(p Person)
  2. func growUp(p Person)

main 函数里先生成 StudentProgrammer 的对象,再将它们分别传入到函数 whatJobgrowUp。函数中,直接调用接口函数,实际执行的时候是看最终传入的实体类型是什么,调用的是实体类型实现的函数。于是,不同对象针对同一消息就有多种表现,多态就实现了。

更深入一点来说的话,在函数 whatJob() 或者 growUp() 内部,接口 person 绑定了实体类型 *Student 或者 Programmer。根据前面分析的 iface 源码,这里会直接调用 fun 里保存的函数,类似于: s.tab->fun[0],而因为 fun 数组里保存的是实体类型实现的函数,所以当函数传入不同的实体类型时,调用的实际上是不同的函数实现,从而实现多态。

运行一下代码:

  1. I am a student.
  2. {19}
  3. I am a programmer.
  4. {100}

参考资料

【各种面向对象的名词】https://cyent.github.io/golang/other/oo/

【多态与鸭子类型】https://www.jb51.net/article/116025.htm